using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

/* DNN Specific modules */
using DotNetNuke.Entities.Portals;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Tabs;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Definitions;
using DotNetNuke.Services.Localization;

/* ifinity specific references */
using DotNetNuke.Providers.SearchEngineSiteMapProvider;
using iFinity.DNN.Modules.NewsArticlesSiteMapProvider.Entities;
using iFinity.DNN.Modules.NewsArticlesSiteMapProvider;

namespace iFinity.DNN.Modules.NewsArticlesSiteMapProvider
{
    public class NewsArticlesSiteMapProvider : SiteMapProvider 
    {
        #region protected members
        protected int _tabID = 0;
        protected bool _singlePagesOnly;
        protected bool _featuredOnly;
        protected bool _notFeaturedOnly;
        protected bool _showHiddenAndExpired;
        protected bool _useShortUrls;
        protected string _shorternId;
        protected bool _showCategories;
        protected int _maxNumArticles = 0;
        protected bool _showSecured = false;
        #endregion

        public NewsArticlesSiteMapProvider(string name, System.Collections.Specialized.NameValueCollection config)
            : base(name, config)
        {
            
            _singlePagesOnly = GetSafeSetting(config, "singlePagesOnly", false);
            _showCategories = GetSafeSetting(config, "showCategories", false);
            _maxNumArticles = GetSafeSetting(config, "maxNumArticles", Null.NullInteger);
            _featuredOnly = GetSafeSetting(config, "featuredOnly", false);
            _showHiddenAndExpired = GetSafeSetting(config, "showHiddenAndExpired", false);
            _showSecured = GetSafeSetting(config, "showSecured", false);
        }

        public override List<SitePage> SitePagesForTab(PortalAliasInfo portalAlias, TabInfo tab, Locale locale, int sitemapNum, int urlCount, int startingCount, int endingCount)
        {
            return GetSitePages(portalAlias, tab, locale, sitemapNum, urlCount, startingCount, endingCount);
        }
        private List<SitePage> GetSitePages(PortalAliasInfo portalAlias, TabInfo tab, Locale locale, int sitemapNum, int urlCount, int startingCount, int endingCount)
        {
            List<SitePage> sitePages = new List<SitePage>();
            try
            {
                //635: get the sitemap page priority from the tab if it is avialable
                _defaultPagePriority = GetTabPriority(_defaultPagePriority, tab);
                _tabID = tab.TabID;
                sitePages = GetPageURLsForArticles(portalAlias, tab, locale, sitemapNum, urlCount, startingCount, endingCount);
            }
            catch (Exception ex)
            {
                sitePages.Add(new SitePageException(ex, this.Debug));
                if (this.Debug)
                    this.DebugMessage("Exception:" + ex.Message + " Trace: " + ex.StackTrace);
            }
            return sitePages;
        }
        /// <summary>
        /// Returns a list of URL's for the possible combination of articles on this page/tab.
        /// </summary>
        /// <param name="siteURL">the URL for the site</param>
        /// <returns>List of SitePage objects</returns>
        private List<SitePage> GetPageURLsForArticles(PortalAliasInfo portalAlias, TabInfo tab, Locale locale, int sitemapNum, int urlCount, int startingCount, int endingCount)
        {
            base.SetBaseURL(portalAlias.HTTPAlias); //get the base url and portal alias url
            //initialise the master collection of SitePage objects
            List<SitePage> pageURLs = new List<SitePage>();
            //get the tab, portal and module information
            _portalAliasURL = portalAlias.HTTPAlias;
            if (portalAlias == null)
                throw new Exception("No PortalInfo found for URL (" + _portalAliasURL + ")");
            ModuleController modCtl = new ModuleController();
            ModuleDefinitionController modDefs = new ModuleDefinitionController();
            //get the list of modules for the page
            tab.Modules = modCtl.GetPortalTabModules(portalAlias.PortalID, tab.TabID);
            if (tab.Modules != null)
            {
                foreach (ModuleInfo mod in tab.Modules)
                {
                    //check each module on the page, looking for the article-specific ones
                    List<SitePage> modulePages = new List<SitePage>();  //instantiate new list of pages
                    ModuleDefinitionInfo modDef = modDefs.GetModuleDefinition(mod.ModuleDefID); //get the module definition for this module
                    string modDefFriendlyName = modDef.FriendlyName;
                    if (base.Name.Contains(modDefFriendlyName))  //check that the module name matches with the name of this provider
                    {
                        //it does, so add in the page URLS per article
                        pageURLs.AddRange(GetLatestNewsArticles(modDef, mod, tab, locale, sitemapNum, urlCount, startingCount, endingCount));
                    }
                    
                }
            }
            return pageURLs;

        }
        /// <summary>
        /// This method gets the List of SitePages for the tab/module combination, where an Articles module 
        /// has been found
        /// </summary>
        /// <param name="modDef">The moduleDef object for the module</param>
        /// <param name="mod">The module Object for the module</param>
        /// <param name="tab">The tab object for the page/tab</param>
        /// <returns>List of SitePage objects</returns>
        private List<SitePage> GetLatestNewsArticles(ModuleDefinitionInfo modDef, ModuleInfo mod, TabInfo tab, Locale locale, int sitemapNum, int urlCount, int startingCount, int endingCount)
        {
            List<SitePage> pageURLs = new List<SitePage>();
            if ((tab.IsVisible || (tab.IsVisible == false && _includeHiddenPages == true))
             && (tab.DisableLink == false || (tab.DisableLink == true && _includeDisabledPages == true)))
            {
                //pull settings from the module settings
                int authorId;
                string replaceChar, shorternId;
                bool useShortUrls;
                int[] categoriesParm;
                GetModuleSettingsFromNewsArticleModule(tab.TabID, mod.ModuleID, mod.TabModuleID, out replaceChar, out useShortUrls,  out shorternId, out authorId, out  categoriesParm);

                int pageSize = endingCount - urlCount; //don't retrieve more than is necessary
                DateTime currentDate = DateTime.Now;
                List<ArticleInfo> articles = null;
                iFinity.DNN.Modules.NewsArticlesSiteMapProvider.Data.DataProvider.Instance().GetNewsArticlesForModule(mod.PortalID, mod.ModuleID, categoriesParm, authorId, _maxNumArticles
                                                                                    , _featuredOnly, _showHiddenAndExpired, _showSecured, _singlePagesOnly, currentDate, out articles);

                DateTime latestOverall = DateTime.MinValue;
                if (articles != null)
                {
                    int pageNum =1; int lastArticleId = 0;
                    foreach (ArticleInfo article in articles)
                    {
                        if (article.ArticleId == lastArticleId)
                            //stil same article
                            pageNum ++;
                        else
                            pageNum = 1;

                        //we've got the articles here, so call down and get the individual list of sitePage objects
                        //for each article in the articles module
                        SitePage articlePage = CreateSitePageFromArticlePage(tab, article, pageNum, locale, replaceChar);
                        pageURLs.Add(articlePage);
                        //keep track of the most recent update overall
                        if (article.LastUpdate > latestOverall)
                            latestOverall = article.LastUpdate;
                        
                        lastArticleId = article.ArticleId;
                    }
                }
                if (_showCategories)
                {
                    //we also need to add an entry for the categories page
                    string[] queryStringItems = new string[2];
                    queryStringItems[0] = "articleType";
                    queryStringItems[1] = "Categories";
                    string catURL = MakeURL(tab, queryStringItems, string.Empty, locale);
                    SitePage categoriesPages = new SitePage(catURL, latestOverall, this.DefaultPageUpdateFrequency, _defaultPagePriority);
                    pageURLs.Add(categoriesPages);
                }
            }
            return pageURLs;

        }

        private void GetModuleSettingsFromNewsArticleModule(int tabId, int moduleId, int tabModuleId, out string replaceChar, out bool useShortUrls, out string shorternId, out int authorId, out int[] categoriesParm)
        {
            //defaults
            authorId = Null.NullInteger;
            replaceChar = "-";
            shorternId = "ID";
            useShortUrls = false;

            ModuleController mc = new ModuleController();
            //get the tab module settings
            Hashtable tabModuleSettings = mc.GetTabModuleSettings(tabModuleId);
            Hashtable moduleSettings = mc.GetModuleSettings(moduleId);

            //for this tabModule, find any filtering settings
            string catKey = "Categories" + tabId.ToString();
            List<int> categoryIds = new List<int>();

            string categories = (string)GetFromSettings(moduleSettings, tabModuleSettings, catKey, null);
            if (categories != null)
            {
                string[] cats = categories.Split(',');
                foreach (string cat in cats)
                {
                    int catId = -1;
                    if (Int32.TryParse(cat, out catId))
                        categoryIds.Add(catId);
                }
            }
            string authorRaw = (string)GetFromSettings(moduleSettings, tabModuleSettings, "Author", "-1");
            if (!Int32.TryParse(authorRaw, out authorId))
                authorId = Null.NullInteger;

            string titleReplacementType = (string)GetFromSettings(moduleSettings, tabModuleSettings, "TitleReplacementType", "-");
            if (titleReplacementType == "Underscore")
                replaceChar = "_";

            string seoUrlMode = (string)GetFromSettings(moduleSettings, tabModuleSettings, "SEOUrlMode", "Normal");
            if (seoUrlMode == "Shortened")
                useShortUrls = true;
            if (useShortUrls)
                shorternId = (string)GetFromSettings(moduleSettings, tabModuleSettings, "SEOShorternID", "ID");

            //determine if we are going to search by category, or just get all articles.
            //-1 in the category Id means 'all categories' has been ticked in the 'category filter'
            if (categoryIds.Count == 0 || categoryIds[0] == -1)//695 : no categories list meant index out of range error
                categoriesParm = null;
            else
                categoriesParm = categoryIds.ToArray();
        }
        private object GetFromSettings(Hashtable moduleSettings, Hashtable tabModuleSettings, string name, object defaultValue)
        {
            object result = defaultValue;
            if (moduleSettings.ContainsKey(name))
                result= moduleSettings[name];
            else
                if (tabModuleSettings.ContainsKey(name))
                    result = tabModuleSettings[name];


            return result;
        }
        //private void GetPageNumAndSize(int moduleId, int sitemapNum, int urlCount, int startingCount, int endingCount, out int pageNum, out int pageSize, out int pagesToGet)
        //{
        //    //get the range of remaining values
        //    pageNum = 1; pageSize = 1; pagesToGet = 1;
            
        //    //now, did we do this once before? 
        //    string key = "pageSize:" + moduleId.ToString();
        //    string rawVal = (string)DotNetNuke.Common.Utilities.DataCache.GetCache(key);
        //    if (!int.TryParse(rawVal, out pageSize))
        //    {
        //        pageSize = endingCount - urlCount; //this is how many articles (max) we can generate for this sitemap
        //    }
        //    else
        //    {
        //        //the page size has been retrieved.  
        //        //so we look at how many records to retrieve (all possible) and then
        //        //work out how many pages to retrieve
        //        int totalRecords = endingCount - urlCount;
        //        pagesToGet = (totalRecords + pageSize - 1) / pageSize; // get the number of pages needed
        //        //the page num depends on the sitemapnum.  SItemap 1 *always*
        //    }
        //    if (sitemapNum == 1)//only store the page size for the first sitemap
        //        DotNetNuke.Common.Utilities.DataCache.SetCache(key, pageSize.ToString());

        //    //string thisPage = (string)DotNetNuke.Common.Utilities.DataCache.GetCache(key);
        //    //if (sitemapNum > 1)
        //    //{
        //    //    string lastPage = (string)DotNetNuke.Common.Utilities.DataCache.GetCache(prevPage);
        //    //    if (lastPage != null)
        //    //    {

        //    //    }
        //    //}
        //    //if (thisPage == null)
        //    //{
        //    //    //nothing there

                
        //    //}

        //    ////form up the value store
        //    //thisPage = PageNumSizeEtc(pageNum, pageSize, pagesToGet);

            

        //}
        //private string PageNumSizeEtc(int pageNum, int pageSize, int pagesToGet)
        //{
        //    return pageNum.ToString() + ":" + pageSize.ToString() + ":" + pagesToGet.ToString();
        //}
        //private void PageNumSizeEtc(string value,out int pageNum, out int pageSize, out int pagesToGet)
        //{
        //    pageNum = 1;
        //    pagesToGet = 1;
        //    pageSize = 0;
        //    if (value != null)
        //    {
        //        string[] values = value.Split(',');
        //        if (values.GetUpperBound(0) > 0)
        //        {
        //            int.TryParse(values[0], out pageNum);
        //            int.TryParse(values[1], out pageSize);
        //            int.TryParse(values[2], out pagesToGet);
        //        }
        //    }
        //}
        //private IList GetArticleList(int moduleId, bool isApproved)
        //{
        //    ArticleController vac = new ArticleController();
        //    IList articles = null;
        //    //find all methods in the type
        //    MethodInfo[] methods = vac.GetType().GetMethods();
        //    Dictionary<string, Type> types = null;
        //    foreach (MethodInfo method in methods)
        //    {
        //        if (method.Name == "GetArticleList")
        //        {
        //            ParameterInfo[] parameters = method.GetParameters();
        //            if (parameters.GetUpperBound(0) == 1)
        //            {
        //                if (parameters[0].Name.ToLower() == "moduleid"
        //                && parameters[1].Name.ToLower() == "isapproved")
        //                {
        //                    //this is the call
        //                    List<object> parmValues = new List<object>();
        //                    parmValues.Add(moduleId);
        //                    parmValues.Add(isApproved);
        //                    articles = (IList)method.Invoke(vac, parmValues.ToArray ());
        //                    break;
        //                }
        //            }
        //        }
        //    }
        //    return articles;
        //}
                                                
        //private IList GetArticleList(int moduleId, int[] categoryIds, bool matchAll
        //                               , int maxNumArticles, int maxAge, bool isApproved, bool isDraft
        //                               , DateTime startDate, string keywords, bool showPending
        //                               , bool showExpired, int authorId
        //                               , bool featuredOnly, bool notFeaturedOnly
        //                               , bool showSecuredONly, bool showNotSecuredOnly
        //                               , string sortBy, string sortDirection, int month, int year
        //                               , int pageNumber, int pageSize, DateTime currentDate, DateTime agedDate)
        //{
        //    //this whole method exists because the VEntrian Article Module does not
        //    //use versioning, and frequently breaks binary compatibility with older
        //    //versions when new version are released.  Because of this, the module
        //    //is called using reflection, so that this single provider cna support 
        //    //multiple news articles versions.
        //    //Yes, it is inefficient compared to directly compiled references,
        //    //but that's the way it is.
        //    ArticleController vac = new ArticleController();
        //    IList articles = null;
        //    //find all methods in the type
        //    MethodInfo[] methods = vac.GetType().GetMethods();
        //    Dictionary<string, Type> types = null;
        //    foreach (MethodInfo method in methods)
        //    {
        //        //look for the 'GetArticleList' method, and when found, match the correct overload
        //        //for the version (we guess the version based on the overload, because the
        //        //product version, assembly version and file version are not set in the Articles assembly
        //        if (method.Name == "GetArticleList")
        //        {
        //            ParameterInfo[] parameters = method.GetParameters();
        //            bool matched = false;
        //            //match the types and the available methods for different versions, starting with the latest and working backwards                    
        //            types = v00703Types();
        //            int vernum = 00703;
        //            matched = CompareTypeList(types, parameters);

        //            if (!matched)
        //            {
        //                types = v00615Types();
        //                vernum = 00615;
        //                matched = CompareTypeList(types, parameters);
        //            }
        //            if (!matched)
        //            {
        //                types = v00610Types();
        //                vernum = 00610;
        //                matched = CompareTypeList(types, parameters);
        //            }
        //            if (!matched)
        //            {
        //                types = v00601Types();
        //                vernum = 00601;
        //                matched = CompareTypeList(types, parameters);
        //            }
                        
        //            if (matched)
        //            {
        //                //this method takes the supplied parameters (which is the superset for the latest version)
        //                //and puts them into an array of values suitable for calling the ArticleController instance.
        //                object[] parmValues = FillParameters(vernum, types, moduleId, categoryIds, matchAll
        //                                       , maxNumArticles, maxAge, isApproved, isDraft
        //                                       , startDate, keywords, showPending
        //                                       , showExpired, authorId
        //                                       , featuredOnly, notFeaturedOnly
        //                                       , showSecuredONly, showNotSecuredOnly
        //                                       , sortBy, sortDirection, month, year, pageNumber, pageSize);
        //                //call by reflection
        //                articles = (IList)method.Invoke(vac, parmValues);
        //                break;
        //            }  
        //        }
        //    }
        //    return articles;
        //}

        //private object[] FillParameters(int vernum, Dictionary<string, Type> types 
        //    , int moduleId, int[] categoryIds, bool matchAll, int maxCount, int maxAge
        //    , bool isApproved, bool isDraft, DateTime startDate, string keywords
        //    , bool showPending, bool showExpired, int authorId, bool featuredOnly
        //    , bool notFeaturedOnly, bool showSecuredOnly, bool showNotSecuredOnly
        //    , string sortBy, string sortDirection, int month, int year, int pageNumber, int pageSize)
        //{

        //    ArrayList values = new ArrayList();
        //    if (vernum > 0700)
        //    {
        //        /* new values ver 07.03 */
        //        /*public System.Collections.Generic.List<Ventrian.NewsArticles.ArticleInfo> GetArticleList(
        //     * int moduleID, System.DateTime currentDate, System.DateTime agedDate, 
        //     * int[ ] categoryID, bool matchAll, int maxCount, 
        //     * int pageNumber, int pageSize, string sortBy, string sortDirection, 
        //     * bool isApproved, bool isDraft, string keywords, int authorID, 
        //     * bool showPending, bool showExpired, bool showFeaturedOnly, bool showNotFeaturedOnly, 
        //     * bool showSecuredOnly, bool showNotSecuredOnly, string articleIDs, ref int totalRecords)
        //        Member of Ventrian.NewsArticles.ArticleController*/
        //        //0 - moduleid
        //        values.Add(moduleId);
        //        if (types.ContainsKey("currentDate"))
        //            values.Add(DateTime.Now);
        //        if (types.ContainsKey("agedDate"))
        //            values.Add(Null.NullDate);

        //        values.Add(categoryIds);
        //        if (types.ContainsKey("matchAll"))
        //            values.Add(matchAll);
        //        if (types.ContainsKey("maxCount"))
        //            values.Add(maxCount);

        //        if (types.ContainsKey("pageNumber"))
        //            values.Add(pageNumber);
        //        if (types.ContainsKey("pageSize"))
        //            values.Add(pageSize);
        //        if (types.ContainsKey("sortBy"))
        //            values.Add(sortBy);
        //        if (types.ContainsKey("sortDirection"))
        //            values.Add(sortDirection);

        //        if (types.ContainsKey("isApproved"))
        //            values.Add(isApproved);
        //        if (types.ContainsKey("isDraft"))
        //            values.Add(isDraft);
        //        if (types.ContainsKey("keywords"))
        //            values.Add(keywords);
        //        if (types.ContainsKey("authorId"))
        //            values.Add(authorId);

        //        if (types.ContainsKey("showPending"))
        //            values.Add(showPending);
        //        if (types.ContainsKey("showExpired"))
        //            values.Add(showExpired);
        //        if (types.ContainsKey("showFeaturedOnly"))
        //            values.Add(featuredOnly);
        //        if (types.ContainsKey("showNotFeaturedOnly"))
        //            values.Add(notFeaturedOnly);

        //        if (types.ContainsKey("showSecuredOnly"))
        //            values.Add(showSecuredOnly);
        //        if (types.ContainsKey("showNotSecuredOnly"))
        //            values.Add(showNotSecuredOnly);
        //        if (types.ContainsKey("articleIds"))
        //            values.Add(Null.NullString);
        //        if (types.ContainsKey("totalRecords"))
        //            values.Add(0);
        //    }
        //    else
        //    {
        //        //0 - moduleid
        //        values.Add(moduleId);
        //        //1 - categoryIds
        //        values.Add(categoryIds);
        //        if (types.ContainsKey("matchAll"))
        //            values.Add(matchAll);
        //        if (types.ContainsKey("maxCount"))
        //            values.Add(maxCount);
        //        if (types.ContainsKey("maxAge"))
        //            values.Add(maxAge);
        //        if (types.ContainsKey("isApproved"))
        //            values.Add(isApproved);
        //        if (types.ContainsKey("isDraft"))
        //            values.Add(isDraft);
        //        if (types.ContainsKey("startDate"))
        //            values.Add(startDate);
        //        if (types.ContainsKey("keywords"))
        //            values.Add(keywords);
        //        if (types.ContainsKey("showPending"))
        //            values.Add(showPending);
        //        if (types.ContainsKey("showExpired"))
        //            values.Add(showExpired);
        //        if (types.ContainsKey("authorId"))
        //            values.Add(authorId);
        //        if (types.ContainsKey("showFeaturedOnly"))
        //            values.Add(featuredOnly);
        //        if (types.ContainsKey("showNotFeaturedOnly"))
        //            values.Add(notFeaturedOnly);
        //        if (types.ContainsKey("showSecuredOnly"))
        //            values.Add(showSecuredOnly);
        //        if (types.ContainsKey("showNotSecuredOnly"))
        //            values.Add(showNotSecuredOnly);
        //        if (types.ContainsKey("sortBy"))
        //            values.Add(sortBy);
        //        if (types.ContainsKey("sortDirection"))
        //            values.Add(sortDirection);
        //        if (types.ContainsKey("month"))
        //            values.Add(month);
        //        if (types.ContainsKey("year"))
        //            values.Add(year);
        //    }
        //    return values.ToArray();
        //}
        //private bool CompareTypeList(Dictionary<string, Type> types, ParameterInfo[] parameters)
        //{
        //    bool same=false;
        //    if (types.Count == parameters.GetUpperBound(0) + 1)
        //    {
        //        same = true; int i = 0;
        //        foreach (Type type in types.Values)
        //        {
        //            ParameterInfo parameter = parameters[i];
        //            if (type != parameter.ParameterType)
        //            {
        //                //have to get through the entire list and match all to be the same
        //                same = false;
        //                break;
        //            }
        //            i++;
        //        }
        //    }
        //    return same;
        //}
        //private Dictionary<string, Type> v00601Types()
        //{
        //    //public System.Collections.ArrayList GetArticleList(int moduleID, int[] categoryID
        //    //, int maxCount, int maxAge, bool isApproved, System.DateTime startDate
        //    //, string keywords, bool showHiddenAndExpired, int authorID
        //    //, bool showFeaturedOnly, string sortBy, int month, int year)
        //    Dictionary<string, Type> types = new Dictionary<string, Type>();
        //    types.Add("moduleId",typeof(int));  //moduleid
        //    types.Add("categoryId",typeof(int[])); //categoryid
        //    //types.Add("matchAll",typeof(bool)); //matchAll
        //    types.Add("maxCount",typeof(int));  //maxcount
        //    types.Add("maxAge",typeof(int)); //maxage
        //    types.Add("isApproved",typeof(bool));//isapproved
        //    //types.Add("isDraft", typeof(bool));//isDraft
        //    types.Add("startDate", typeof(DateTime));//startDate
        //    types.Add("keywords",typeof(string)); //keywords
        //    //types.Add("showPending",typeof(bool)); //showPending
        //    types.Add("showExpired",typeof(bool)); //showExpired
        //    types.Add("authorId",typeof(int)); //authorId
        //    types.Add("showFeaturedOnly",typeof(bool)); //showfeaturedonly
        //    //types.Add("showNotFeaturedOnly",typeof(bool)); //shownotfeaturedonly
        //    //types.Add("showSecuredOnly",typeof(bool)); //showSEcuredOnly
        //    //types.Add("showNotSecuredOnly",typeof(bool)); //showNotsecuredonly
        //    types.Add("sortBy",typeof(string)); //sortBy
        //    //types.Add("sortDirection",typeof(string)); //sortDirection
        //    types.Add("month",typeof(int)); //month
        //    types.Add("year",typeof(int)); //year
        //    return types;
        //}
        //private Dictionary<string, Type> v00610Types()
        //{
        //    //public System.Collections.ArrayList GetArticleList(int moduleID, int[ ] categoryID
        //    //, bool matchAll, int maxCount, int maxAge, bool isApproved, bool isDraft
        //    //, System.DateTime startDate, string keywords, bool showPending, bool showExpired
        //    //, int authorID, bool showFeaturedOnly, bool showNotFeaturedOnly, string sortBy
        //    //, string sortDirection, int month, int year)
        //    Dictionary<string, Type> types = new Dictionary<string, Type>();
        //    types.Add("moduleId", typeof(int));  //moduleid
        //    types.Add("categoryId", typeof(int[])); //categoryid
        //    types.Add("matchAll",typeof(bool)); //matchAll
        //    types.Add("maxCount", typeof(int));  //maxcount
        //    types.Add("maxAge", typeof(int)); //maxage
        //    types.Add("isApproved", typeof(bool));//isapproved
        //    types.Add("isDraft", typeof(bool));//isDraft
        //    types.Add("startDate", typeof(DateTime));//startDate
        //    types.Add("keywords", typeof(string)); //keywords
        //    types.Add("showPending",typeof(bool)); //showPending
        //    types.Add("showExpired", typeof(bool)); //showExpired
        //    types.Add("authorId", typeof(int)); //authorId
        //    types.Add("showFeaturedOnly", typeof(bool)); //showfeaturedonly
        //    types.Add("showNotFeaturedOnly",typeof(bool)); //shownotfeaturedonly
        //    //types.Add("showSecuredOnly",typeof(bool)); //showSEcuredOnly
        //    //types.Add("showNotSecuredOnly",typeof(bool)); //showNotsecuredonly
        //    types.Add("sortBy", typeof(string)); //sortBy
        //    types.Add("sortDirection",typeof(string)); //sortDirection
        //    types.Add("month", typeof(int)); //month
        //    types.Add("year", typeof(int)); //year
        //    return types;
        //}
        //private Dictionary<string, Type> v00615Types()
        //{
        //    //Public Function GetArticleList(ByVal moduleID As Integer, ByVal categoryID As Integer()
        //    //, ByVal matchAll As Boolean, ByVal maxCount As Integer, ByVal maxAge As Integer
        //    //, ByVal isApproved As Boolean, ByVal isDraft As Boolean, ByVal startDate As DateTime
        //    //, ByVal keywords As String, ByVal showPending As Boolean, ByVal showExpired As Boolean
        //    //, ByVal authorID As Integer, ByVal showFeaturedOnly As Boolean, ByVal showNotFeaturedOnly As Boolean
        //    //, ByVal showSecuredOnly As Boolean, ByVal showNotSecuredOnly As Boolean, ByVal sortBy As String
        //    //, ByVal sortDirection As String, ByVal month As Integer, ByVal year As Integer) As ArrayList
        //    Dictionary<string, Type> types = new Dictionary<string, Type>();
        //    types.Add("moduleId", typeof(int));  //moduleid
        //    types.Add("categoryId", typeof(int[])); //categoryid
        //    types.Add("matchAll",typeof(bool)); //matchAll
        //    types.Add("maxCount", typeof(int));  //maxcount
        //    types.Add("maxAge", typeof(int)); //maxage
        //    types.Add("isApproved", typeof(bool));//isapproved
        //    types.Add("isDraft", typeof(bool));//isDraft
        //    types.Add("startDate", typeof(DateTime));//startDate
        //    types.Add("keywords", typeof(string)); //keywords
        //    types.Add("showPending",typeof(bool)); //showPending
        //    types.Add("showExpired", typeof(bool)); //showExpired
        //    types.Add("authorId", typeof(int)); //authorId
        //    types.Add("showFeaturedOnly", typeof(bool)); //showfeaturedonly
        //    types.Add("showNotFeaturedOnly",typeof(bool)); //shownotfeaturedonly
        //    types.Add("showSecuredOnly",typeof(bool)); //showSEcuredOnly
        //    types.Add("showNotSecuredOnly",typeof(bool)); //showNotsecuredonly
        //    types.Add("sortBy", typeof(string)); //sortBy
        //    types.Add("sortDirection",typeof(string)); //sortDirection
        //    types.Add("month", typeof(int)); //month
        //    types.Add("year", typeof(int)); //year
        //    return types;
        //}
        //private Dictionary<string, Type> v00703Types()
        //{
        //    /*public System.Collections.Generic.List<Ventrian.NewsArticles.ArticleInfo> GetArticleList(
        //     * int moduleID, System.DateTime currentDate, System.DateTime agedDate, 
        //     * int[ ] categoryID, bool matchAll, int maxCount, 
        //     * int pageNumber, int pageSize, string sortBy, string sortDirection, 
        //     * bool isApproved, bool isDraft, string keywords, int authorID, 
        //     * bool showPending, bool showExpired, bool showFeaturedOnly, bool showNotFeaturedOnly, 
        //     * bool showSecuredOnly, bool showNotSecuredOnly, string articleIDs, ref int totalRecords)
        //        Member of Ventrian.NewsArticles.ArticleController*/

        //    Dictionary<string, Type> types = new Dictionary<string, Type>();
        //    types.Add("moduleId", typeof(int));  //moduleid
        //    types.Add("currentDate", typeof(DateTime)); //currentDate
        //    types.Add("agedDate", typeof(DateTime)); //agedDate
            
        //    types.Add("categoryId", typeof(int[])); //categoryid
        //    types.Add("matchAll", typeof(bool)); //matchAll
        //    types.Add("maxCount", typeof(int));  //maxcount

        //    types.Add("pageNumber", typeof(int));
        //    types.Add("pageSize", typeof(int));
        //    types.Add("sortBy", typeof(string)); //sortBy
        //    types.Add("sortDirection", typeof(string)); //sortDirection

        //    types.Add("isApproved", typeof(bool));//isapproved
        //    types.Add("isDraft", typeof(bool));//isDraft
        //    types.Add("keywords", typeof(string)); //keywords
        //    types.Add("authorId", typeof(int)); //authorId

        //    types.Add("showPending", typeof(bool)); //showPending
        //    types.Add("showExpired", typeof(bool)); //showExpired
        //    types.Add("showFeaturedOnly", typeof(bool)); //showfeaturedonly
        //    types.Add("showNotFeaturedOnly", typeof(bool)); //shownotfeaturedonly

        //    types.Add("showSecuredOnly", typeof(bool)); //showSEcuredOnly
        //    types.Add("showNotSecuredOnly", typeof(bool)); //showNotsecuredonly

        //    types.Add("articleIds", typeof(string)); //articleIds
        //    types.Add("totalRecords", typeof(int).MakeByRefType()); //totalrecords
        //    return types;
        //}
        ///// <summary>
        ///// Returns an instance of a SitePage object based on a single article/tab combination
        ///// </summary>
        ///// <param name="article">An ArticleInfo object</param>
        ///// <param name="tab">the TabInfo object for the page</param>
        ///// <returns>A list of SitePage objects, with each one representing a unique URL to an article page</returns>
        //private List<SitePage> CreatePagesFromArticle(ArticleInfo article, TabInfo tab, ModuleInfo mod, Locale locale)
        //{
        //    List<SitePage> articlePageURLs = new List<SitePage>();  //new list of SitePages
        //    //next lot are the controllers/objects for the article page / comments for the article
        //    PageController pc = new PageController(); 
        //    CommentController cc = new CommentController();
        //    ArrayList articlePages = pc.GetPageList(article.ArticleID);
        //    ArrayList articleComments = cc.GetCommentList(mod.ModuleID,article.ArticleID, true);
        //    int pageNum = 0;
        //    //iterate the pages in the article page arrayList
        //    if (articlePages.Count == 0)
        //    {
        //        //still has a url, even if no pages (summary only)
        //        articlePageURLs.Add(CreateSitePageFromArticlePage(tab, article, 1, articleComments, locale));
        //    }
        //    else
        //    {
        //        foreach (PageInfo articlePage in articlePages)
        //        {
        //            pageNum++;
        //            SitePage articleSitePage = CreateSitePageFromArticlePage(tab, article, pageNum, articleComments, locale);
        //            articlePageURLs.Add(articleSitePage);
        //            if (_singlePagesOnly) break; //only do the first page if singlepagesOnly
        //        }
        //    }
        //    return articlePageURLs;
        //}
        private SitePage CreateSitePageFromArticlePage(TabInfo tab, ArticleInfo article, int pageNum, Locale locale, string replaceChar)
        {
            string url = GetArticleUrl(tab, pageNum, article, locale, replaceChar); //get the URL of the tab/article combination
            DateTime mostRecentUpdate;
            DateTime previousUpdate;
            ChangeFrequencies changeFreq = this.DefaultPageUpdateFrequency;
            if (article.CommentCount > 0)  //check for comments on the article
            {
                //check which is more recent - the article update or the comment
                if (article.LatestCommentDate > article.LastUpdate)
                {
                    mostRecentUpdate = article.LatestCommentDate;
                    previousUpdate = article.LastUpdate;
                }
                else
                {
                    mostRecentUpdate = article.LastUpdate;
                    previousUpdate = article.LatestCommentDate;
                }
            }
            else
            {
                //the most recent update is the difference between the creation and update dates
                mostRecentUpdate = article.LastUpdate;
                previousUpdate = article.CreatedDate;
            }
            //get the default priority for the 'regular' article pages.
            double priority = _defaultPagePriority;
            changeFreq = GetArticleUpdateFrequency(mostRecentUpdate, previousUpdate);
            return new SitePage(url, mostRecentUpdate, changeFreq, priority);
        }

        /// <summary>
        /// Returns the unique URL for the article page
        /// </summary>
        /// <param name="tab">A TabInfo object for the page that the Article Module is on</param>
        /// <param name="pageNum">The page number of the article</param>
        /// <param name="article">The ARticleInfo object for the Article</param>
        /// <returns>A string value, dependant on the Friendly URL setting, of the article</returns>
        private string GetArticleUrl(TabInfo tab, int pageNum, ArticleInfo article, Locale locale, string replaceChar)
        {
            string url = base.GetTabUrl(tab);
            string[] queryStringItems;
            string replaceAspxPageName="";
            int queryStringItemsCount = 0;
            if (_useShortUrls)
            {
                if (pageNum == 1)
                {
                    queryStringItemsCount = 2;
                    queryStringItems = new string[2];
                    queryStringItems[0] = _shorternId;
                    queryStringItems[1] = article.ArticleId.ToString();
                    replaceAspxPageName = UrlUtilities.FormatTitle(article.Title, replaceChar);
                    if (replaceAspxPageName.Length == 0)
                        replaceAspxPageName = Globals.glbDefaultPage;
                }
                else
                {
                    queryStringItemsCount = 4;
                    queryStringItems = new string[6];
                    queryStringItems[0] = "PageID";
                    queryStringItems[1] = pageNum.ToString();
                    queryStringItems[2] = "ID";
                    queryStringItems[3] = article.ArticleId.ToString();
                    replaceAspxPageName = DotNetNuke.Common.Globals.glbDefaultPage;
                }
            }
            else
            {
                if (pageNum == 1)
                {
                    queryStringItemsCount = 4;
                    queryStringItems = new string[4];
                    queryStringItems[0] = "articleType";
                    queryStringItems[1] = "ArticleView";
                    queryStringItems[2] = "articleID";
                    queryStringItems[3] = article.ArticleId.ToString();
                    replaceAspxPageName = UrlUtilities.FormatTitle(article.Title, replaceChar);
                    if (replaceAspxPageName.Length == 0)
                        replaceAspxPageName = Globals.glbDefaultPage;
                }
                else
                {
                    queryStringItemsCount = 6;
                    queryStringItems = new string[6];
                    queryStringItems[0] = "PageID";
                    queryStringItems[1] = pageNum.ToString();
                    queryStringItems[2] = "ArticleID";
                    queryStringItems[3] = article.ArticleId.ToString();
                    queryStringItems[4] = "articleType";
                    queryStringItems[5] = "ArticleView";
                    replaceAspxPageName = DotNetNuke.Common.Globals.glbDefaultPage;
                }
            }
            return MakeURL(tab, queryStringItems, replaceAspxPageName, locale);
        }
        /// <summary>
        /// Returns a structured URL with a Querystring based on the input base Tab URL, and each of the name/value pairs in the 
        /// Query string.  This method will automatically determine whether or not friendly URL's are required.
        /// </summary>
        /// <param name="tabURL">The base tab URL for the site : tabname/nn/default.aspx or default.aspx?tabid=55</param>
        /// <param name="queryStringItems">A string array of name value pairs, where [i]=name, [i+1]=value</param>
        /// <param name="aspxPageName">The name to replace default.aspx with, if necessary, otherwise string.empty</param>
        /// <returns>a formatted string of the base URL plus the query string values</returns>
        private string MakeURL(TabInfo tab, string[] queryStringItems, string aspxPageName, Locale locale)
        {
            string url ;
            if (_friendlyURLs == true)
            {
                if (aspxPageName == null || aspxPageName == string.Empty)
                    aspxPageName = DotNetNuke.Common.Globals.glbDefaultPage;
                //build up the path and query string for submitting to the friendly Url provider
                string path = "~/" + DotNetNuke.Common.Globals.glbDefaultPage + "?tabid=" + tab.TabID.ToString();
                bool key=true; string qs=""; string qsPair="";
                foreach (string qsBit in queryStringItems)
                {
                    if (key)
                    {
                        qsPair = qsBit;
                        key = false;
                    }
                    else
                    {
                        qsPair += "=" + qsBit;
                        qs += "&" + qsPair;
                        key = true;
                    }
                }
                path += qs;

                //only add language to url if more than one locale is enabled, and if admin did not turn it off
                if (Localization.GetEnabledLocales().Count > 1 && Localization.UseLanguageInUrl() == true)
                {
                    if (locale == null || locale.Code == "")
                        path += "&language=" + System.Threading.Thread.CurrentThread.CurrentCulture.Name;
                    else
                        path += "&language=" + locale.Code;
                }

                url = DotNetNuke.Services.Url.FriendlyUrl.FriendlyUrlProvider.Instance().FriendlyUrl(tab, path, aspxPageName, _portalSettings);
                
            }
            else
            {
                url = DotNetNuke.Common.Globals.NavigateURL(tab.TabID, "", queryStringItems);
            }
            if (!url.Contains("http://"))
            {
                if (!url.Contains(_portalAliasURL))
                {
                    url = _portalAliasURL + url;
                    if (!url.Contains(_portalAliasURL + "/"))
                        url = url.Replace(_portalAliasURL, _portalAliasURL + "/");
                }
                if (!url.StartsWith("http://"))
                    url = "http://" + url;
            }
            return url;
        }
        ///// <summary>
        ///// Pulls the tab ID out of a supplied URL.
        ///// </summary>
        ///// <param name="siteURL">The URL of the tab</param>
        //private void StripTabID(string siteURL)
        //{
        //    //we are expecting the siteURL to be a specific tab URL
        //    string[] urlParts = siteURL.Split('/', '?', '=');
        //    //example Articles tab ID
        //    //http://localhost/dnnDev/Articles/tabid/55/Default.aspx
        //    //http://localhost/dnnDev/Articles/tabid/55/articleType/ArticleView/articleId/1/Test-Article-1.aspx
        //    //http://localhost/dnnDev/Articles/tabid/55/PageID/2/ArticleID/1/articleType/ArticleView/Default.aspx
        //    bool nextBitTabID = false, nextBitEntryID = false;
        //    foreach (string urlBit in urlParts)
        //    {
        //        if (nextBitTabID)
        //        {
        //            //this should be the TabID
        //            if (int.TryParse(urlBit, out _tabID))
        //                nextBitTabID = false;
        //        }
        //        switch (urlBit.ToLower())
        //        {
        //            case "tabid":
        //                nextBitTabID = true;
        //                break;
        //        }
        //    }
        //}
        /// <summary>
        /// Returns the update frequency of an article, based on the latest entries to it.
        /// </summary>
        /// <param name="mostRecentUpdate">The most recent datetime of an update to the article</param>
        /// <param name="previousUpdate">The second-most recent datetime of an update to the article</param>
        /// <returns>A ChangeFrequencies enumerated type value.</returns>
        private ChangeFrequencies GetArticleUpdateFrequency(DateTime mostRecentUpdate, DateTime previousUpdate)
        {
            ChangeFrequencies changeFreq = ChangeFrequencies.never;
            //work out how often the article is updated by looking at the frequency of the blog updates
            TimeSpan timeBetween = mostRecentUpdate - previousUpdate;
            if (timeBetween.Days < 2)
                changeFreq = ChangeFrequencies.daily;
            else if (timeBetween.Days < 14)
                changeFreq = ChangeFrequencies.weekly;
            else if (timeBetween.Days < 60)
                changeFreq = ChangeFrequencies.monthly;
            else if (timeBetween.Days < 500)
                changeFreq = ChangeFrequencies.yearly;
            else
                changeFreq = ChangeFrequencies.never;
            return changeFreq;
        }
    }
}
